package ptr_learn

import "fmt"

//http://www.lsdcloud.com/go/introduction.html

//Go语言中的函数传参都是值拷贝，当我们想要修改某个变量的时候，我们可以创建一个指向该变量地址的指针变量。
//传递数据使用指针，而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单，只需要记住两个符号：&（取地址）和*（根据地址取值）。

//todo 指针地址和指针类型
//每个变量在运行时都拥有一个地址，这个地址代表变量在内存中的位置。
//Go语言中使用&字符放在变量前面对变量进行“取地址”操作。
//Go语言中的值类型（int、float、bool、string、array、struct）都有对应的指针类型，如：*int、*int64、*string等。

//ptr := &v    //语法: v的类型为T
//v:代表被取地址的变量，类型为T
//    ptr:用于接收地址的变量，ptr的类型就为*T，称做T的指针类型。*代表指针

//todo 指向指针的指针
//如果一个指针变量存放的又是另一个指针变量的地址，则称这个指针变量为指向指针的指针变量。 当定义一个指向指针的指针变量时，第一个指针存放第二个指针的地址，第二个指针存放变量的地址

func aaaa() {
	var a int
	var ptr *int
	var pptr **int
	a = 3000
	//指针ptr地址
	ptr = &a
	//指向指针ptr地址
	pptr = &ptr
	//获取pptr的值
	fmt.Printf("变量 a = %d\n", a)                  //变量 a = 3000
	fmt.Printf("指针ptr= %v\n", ptr)                //指针ptr= 0xc00000a0b8
	fmt.Printf("取指针变量ptr的值 *ptr= %d\n", *ptr)     //取指针变量ptr的值 *ptr= 3000
	fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr) //指向指针的指针变量 **pptr = 3000
	fmt.Printf("变量的内存地址 a = %v\n", ptr)           //ptr这个值是指向a的内存地址的。 a = 0xc00000a0b8
	fmt.Printf("指针ptr取的内存地址为 &ptr = %v\n", &ptr)  //&ptr这个值为指针ptr的内存地址。  &ptr = 0xc000006028
	fmt.Printf("第一个指针变量的内存地址 *ptr = %v\n", pptr)  //pptr这个值，是指向指针ptr在内存中的地址的。  *ptr = 0xc000006028

}

//todo new
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
//func new(Type) *Type

//    Type表示类型，new函数只接受一个参数，这个参数是一个类型
//    *Type表示类型指针，new函数返回一个指向该类型内存地址的指针。

//new函数不太常用，使用new函数得到的是一个类型的指针，并且该指针对应的值为该类型的零值。

func ddd() {
	a := new(int)         //a就是一个*int（int类型的指针）
	b := new(bool)        //b就是一个*bool （bool类型的指针）
	fmt.Printf("%T\n", a) // *int
	fmt.Printf("%T\n", b) // *bool
	fmt.Println(*a)       // 0
	fmt.Println(*b)       // false
}

//todo make

/*
	make也是用于内存分配的，区别于new，它只用于slice、map以及channel的内存创建，
	而且它返回的类型就是这三个类型本身，而不是他们的指针类型，因为这三种类型就是引用类型，所以就没有必要返回他们的指针了。


	func make(t Type, size ...IntegerType) Type

*/

/**
make函数是无可替代的，我们在使用slice、map以及channel的时候，都需要使用make进行初始化，然后才可以对它们进行操作
*/

//todo new与make的区别

/**

  二者都是用来申请内存的。
  new很少用,一般用来给基本类型申请内存的。并且内存对应的值为类型零值，返回的是指向类型的指针。比如string int返回的是对应类型的指针（*string *int）
  make只用于slice、map以及channel的初始化，返回的还是这三个引用类型本身；
*/
